﻿using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using PdfSharp.Drawing;
using PdfSharp.Drawing.Layout;
using PdfSharp.Pdf;

namespace InvoiceInvoker.Logic
{
	public class PdfCreator
	{
		public enum InvoiceType { Original, Copy, Preview }

		private Invoice _invoice;
		private Seller _seller;
		private Customer _customer;
		private List<Product> _products;
		private PdfDocument _document;
		private static readonly XPen _pen = new XPen(XColors.Black, 0.5);
		private static readonly XBrush _brush = XBrushes.Black;
		private static readonly XFont _fontNormal = new XFont("Arial", 9, XFontStyle.Regular, new XPdfFontOptions(PdfFontEncoding.Unicode));
		private static readonly XFont _fontBold = new XFont("Arial", 9, XFontStyle.Bold, new XPdfFontOptions(PdfFontEncoding.Unicode));
		private static readonly XFont _fontHeader = new XFont("Arial", 14, XFontStyle.Bold, new XPdfFontOptions(PdfFontEncoding.Unicode));
		private static readonly XFont _fontInvoiceType = new XFont("Arial", 12, XFontStyle.Bold, new XPdfFontOptions(PdfFontEncoding.Unicode));
		private static readonly XFont _fontDates = new XFont("Arial", 10, XFontStyle.Bold, new XPdfFontOptions(PdfFontEncoding.Unicode));
		private static readonly XFont _fontProductsHeader = new XFont("Arial", 8, XFontStyle.Bold, new XPdfFontOptions(PdfFontEncoding.Unicode));
		private static readonly XFont _fontSubtitle = new XFont("Arial", 6, XFontStyle.Regular, new XPdfFontOptions(PdfFontEncoding.Unicode));

		public PdfCreator(Invoice invoice)
		{
			_invoice = invoice;
			_seller = invoice.Seller;
			_customer = invoice.Customer;
			_products = invoice.Products.ToList();
			_document = new PdfDocument();
		}

		public void Create(InvoiceType type)
		{
			PdfPage page = _document.AddPage();
			DrawHeader(page, type);
			DrawParticipantsInfo(page);

			double currentPosition = 185; // bottom border position of the previous element
			currentPosition = DrawProductsHeader(page, currentPosition, false);

			for (int i = 0; i != _products.Count; i++)
			{
				if (currentPosition + 12 > page.Height - 28) // 12 == the height of the single product row; 28 == horizontal margin of the page
				{
					page = _document.AddPage();
					currentPosition = DrawProductsHeader(page, 28, false);
				}
				currentPosition = DrawProduct(page, currentPosition, _products[i], i + 1);
			}

			List<string> vatRates = _invoice.GetVatRates();

			currentPosition += 3; // guarantees the proper upper margin of the next element

			if (currentPosition + 12 + vatRates.Count * 12 > page.Height - 28)
			{
				page = _document.AddPage();
				currentPosition = DrawProductsHeader(page, 28, true);
			}

			currentPosition = DrawGeneralAmountsInfo(page, currentPosition, _invoice.GetGeneralNetValue(), _invoice.GetGeneralVatValue(), _invoice.GetGeneralGrossValue());
			DrawVatRatesInfoHeader(page, currentPosition, vatRates.Count);

			foreach (string vatRate in vatRates)
				currentPosition = DrawVatRatesInfo(page, currentPosition, _invoice.GetGeneralNetValue(vatRate), vatRate, _invoice.GetGeneralVatValue(vatRate), _invoice.GetGeneralGrossValue(vatRate));

			currentPosition += 10; // guarantees the proper upper margin of the next element

			double toPayInWordsHeight = MeasureTextHeight(page, "Słownie: " + _invoice.ToPayInWords);

			if (currentPosition + 28 + 7 + toPayInWordsHeight + 20 > page.Height - 28) // 28 == the height of the toPay, paid and leftToPay rectangles ... 
			{																		   // ... 7 == the margin between these rectangles and the toPayInWords rectangle; 20 == the horizontal margins of the toPayInWords text
				page = _document.AddPage();
				currentPosition = 28;
			}

			currentPosition = DrawPaymentInfo(page, currentPosition, toPayInWordsHeight);

			currentPosition += 15; // guarantees the proper upper margin of the next element

			if (string.IsNullOrWhiteSpace(_invoice.Remarks) == false)
			{
				double remarksHeight = MeasureTextHeight(page, "Uwagi: " + _invoice.Remarks);

				if (currentPosition + remarksHeight + 20 > page.Height - 28) // 20 == the horizontal margins of the remarks text
				{
					page = _document.AddPage();
					currentPosition = 28;
				}

				currentPosition = DrawRemarks(page, currentPosition, remarksHeight);
			}

			currentPosition += 15; // guarantees the minimum upper margin of the next element

			if (currentPosition + 70 > page.Height - 28) // 70 == the height of the signature rectangles
			{
				page = _document.AddPage();
				currentPosition = 28;
			}
			else if (currentPosition + 70 + 50 > page.Height - 28) // 50 == the maximum upper margin of the next element
			{
				currentPosition = page.Height - 28 - 70; //  // guarantees the upper margin of the next element will be between minimum and maximum
			}
			else
			{
				currentPosition += 50; // guarantees the maximum upper margin of the next element
			}

			DrawSignatures(page, currentPosition);

			DrawFooters();
		}

		private void DrawHeader(PdfPage page, InvoiceType type)
		{
			using (XGraphics graphics = XGraphics.FromPdfPage(page))
			{
				XRect rectStamp = new XRect(28, 28, 207, 69);
				XRect rectHeader = new XRect(238, 28, 147, 40);
				XRect rectCreationDate = new XRect(238, 71, 147, 26);
				XRect rectInvoiceNumber = new XRect(388, 28, 176, 20);
				XRect rectInvoiceType = new XRect(388, 51, 176, 17);
				XRect rectSaleDate = new XRect(388, 71, 176, 26);

				graphics.DrawRectangle(_pen, rectStamp);
				graphics.DrawRectangle(_pen, XBrushes.LightGray, rectHeader);
				graphics.DrawRectangle(_pen, rectCreationDate);
				graphics.DrawRectangle(_pen, rectInvoiceNumber);
				graphics.DrawRectangle(XPens.White, rectInvoiceType);
				graphics.DrawRectangle(_pen, rectSaleDate);

				XFont fontNumber = _fontDates;
				while (graphics.MeasureString("Nr " + _invoice.Number, fontNumber).Width > 172)
					fontNumber = new XFont("Arial", fontNumber.Size - 0.01, XFontStyle.Bold, new XPdfFontOptions(PdfFontEncoding.Unicode));

				graphics.DrawString("Faktura VAT", _fontHeader, _brush, rectHeader, XStringFormats.Center);

				switch (type)
				{
					case InvoiceType.Original:
						graphics.DrawString("ORYGINAŁ", _fontInvoiceType, _brush, rectInvoiceType, XStringFormats.Center);
						break;
					case InvoiceType.Copy:
						graphics.DrawString("KOPIA", _fontInvoiceType, _brush, rectInvoiceType, XStringFormats.Center);
						break;
					case InvoiceType.Preview:
						graphics.DrawString("PODGLĄD", _fontInvoiceType, _brush, rectInvoiceType, XStringFormats.Center);
						break;
					default: break;
				}

				graphics.DrawString("pieczęć firmy", _fontSubtitle, _brush, rectStamp, XStringFormats.BottomCenter);
				graphics.DrawString("data wystawienia", _fontSubtitle, _brush, rectCreationDate, XStringFormats.BottomCenter);
				graphics.DrawString("data sprzedaży", _fontSubtitle, _brush, rectSaleDate, XStringFormats.BottomCenter);
				graphics.DrawString("Nr " + _invoice.Number, fontNumber, _brush, rectInvoiceNumber, XStringFormats.Center);
				graphics.DrawString(_invoice.CreationDate.Value.ToShortDateString(), _fontDates, _brush, rectCreationDate, XStringFormats.Center);
				graphics.DrawString(_invoice.SaleDate.Value.ToShortDateString(), _fontDates, _brush, rectSaleDate, XStringFormats.Center);
			}
		}

		private void DrawParticipantsInfo(PdfPage page)
		{
			using (XGraphics graphics = XGraphics.FromPdfPage(page))
			{
				XRect rectSeller = new XRect(28, 100, 267, 51);
				XRect rectCustomer = new XRect(298, 100, 266, 51);
				XRect rectPaymentInfo = new XRect(28, 154, 536, 28);

				graphics.DrawRectangle(_pen, rectSeller);
				graphics.DrawRectangle(_pen, rectCustomer);
				graphics.DrawRectangle(_pen, rectPaymentInfo);

				string strSeller = "Sprzedawca:";
				string strCustomer = "Nabywca:";
				string strAddress = "Adres:";
				string strNip = "NIP:";
				string strPaymentType = "Sposób płatności:";
				string strPaymentDeadline = ", Termin płatności:";
				string strBankName = "Bank:";
				string strSwift = ", Swift:";
				string strAccountNumber = ", Numer konta:";

				double strSellerWidth = graphics.MeasureString(strSeller, _fontNormal).Width;
				double strCustomerWidth = graphics.MeasureString(strCustomer, _fontNormal).Width;
				double strAddressWidth = graphics.MeasureString(strAddress, _fontNormal).Width;
				double strNipWidth = graphics.MeasureString(strNip, _fontNormal).Width;
				double strPaymentTypeWidth = graphics.MeasureString(strPaymentType, _fontNormal).Width;
				double strPaymentDeadlineWidth = graphics.MeasureString(strPaymentDeadline, _fontNormal).Width;
				double strBankNameWidth = graphics.MeasureString(strBankName, _fontNormal).Width;
				double strSwiftWidth = graphics.MeasureString(strSwift, _fontNormal).Width;
				double strAccountNumberWidth = graphics.MeasureString(strAccountNumber, _fontNormal).Width;

				double paymentTypeWidth = graphics.MeasureString(_invoice.PaymentType, _fontBold).Width;
				double bankNameWidth = graphics.MeasureString(_seller.BankName, _fontBold).Width;
				double swiftWidth = graphics.MeasureString(_seller.BankSwift, _fontBold).Width;

				graphics.DrawString(strSeller, _fontNormal, _brush, rectSeller.Left + 5, rectSeller.Top + 11);
				graphics.DrawString(_seller.CompanyName, GetBoldFont(_seller.CompanyName, rectSeller.Right - (rectSeller.Left + 9 + strSellerWidth), graphics), _brush, rectSeller.Left + 7 + strSellerWidth, rectSeller.Top + 11);
				graphics.DrawString(strAddress, _fontNormal, _brush, rectSeller.Left + 5, rectSeller.Top + 23);
				graphics.DrawString(_seller.Street, GetBoldFont(_seller.Street, rectSeller.Right - (rectSeller.Left + 9 + strAddressWidth), graphics), _brush, rectSeller.Left + 7 + strAddressWidth, rectSeller.Top + 23);
				graphics.DrawString(_seller.PostalCode + " " + _seller.City, GetBoldFont(_seller.PostalCode + " " + _seller.City, rectSeller.Right - (rectSeller.Left + 9 + strAddressWidth), graphics), _brush, rectSeller.Left + 7 + strAddressWidth, rectSeller.Top + 35);
				graphics.DrawString(strNip, _fontNormal, _brush, rectSeller.Left + 5, rectSeller.Top + 47);
				graphics.DrawString(_seller.Nip, _fontBold, _brush, rectSeller.Left + 7 + strNipWidth, rectSeller.Top + 47);

				graphics.DrawString(strCustomer, _fontNormal, _brush, rectCustomer.Left + 5, rectCustomer.Top + 11);
				graphics.DrawString(_customer.CompanyName, GetBoldFont(_customer.CompanyName, rectCustomer.Right - (rectCustomer.Left + 9 + strCustomerWidth), graphics), _brush, rectCustomer.Left + 7 + strCustomerWidth, rectCustomer.Top + 11);
				graphics.DrawString(strAddress, _fontNormal, _brush, rectCustomer.Left + 5, rectCustomer.Top + 23);
				graphics.DrawString(_customer.Street, GetBoldFont(_customer.Street, rectCustomer.Right - (rectCustomer.Left + 9 + strAddressWidth), graphics), _brush, rectCustomer.Left + 7 + strAddressWidth, rectCustomer.Top + 23);
				graphics.DrawString(_customer.PostalCode + " " + _customer.City, GetBoldFont(_customer.PostalCode + " " + _customer.City, rectCustomer.Right - (rectCustomer.Left + 9 + strAddressWidth), graphics), _brush, rectCustomer.Left + 7 + strAddressWidth, rectCustomer.Top + 35);
				graphics.DrawString(strNip, _fontNormal, _brush, rectCustomer.Left + 5, rectCustomer.Top + 47);
				graphics.DrawString(_customer.Nip, _fontBold, _brush, rectCustomer.Left + 7 + strNipWidth, rectCustomer.Top + 47);

				graphics.DrawString(strPaymentType, _fontNormal, _brush, rectPaymentInfo.Left + 5, rectPaymentInfo.Top + 11);
				graphics.DrawString(_invoice.PaymentType, _fontBold, _brush, rectPaymentInfo.Left + 7 + strPaymentTypeWidth, rectPaymentInfo.Top + 11);
				graphics.DrawString(strPaymentDeadline, _fontNormal, _brush, rectPaymentInfo.Left + 7 + strPaymentTypeWidth + paymentTypeWidth, rectPaymentInfo.Top + 11);
				graphics.DrawString(_invoice.PaymentDeadline.Value.ToShortDateString(), _fontBold, _brush, rectPaymentInfo.Left + 9 + strPaymentTypeWidth + paymentTypeWidth + strPaymentDeadlineWidth, rectPaymentInfo.Top + 11);
				graphics.DrawString(strBankName, _fontNormal, _brush, rectPaymentInfo.Left + 5, rectPaymentInfo.Top + 23);
				graphics.DrawString(_seller.BankName, _fontBold, _brush, rectPaymentInfo.Left + 7 + strBankNameWidth, rectPaymentInfo.Top + 23);
				if (_seller.BankSwift != string.Empty)
				{
					graphics.DrawString(strSwift, _fontNormal, _brush, rectPaymentInfo.Left + 7 + strBankNameWidth + bankNameWidth, rectPaymentInfo.Top + 23);
					graphics.DrawString(_seller.BankSwift, _fontBold, _brush, rectPaymentInfo.Left + 9 + strBankNameWidth + bankNameWidth + strSwiftWidth, rectPaymentInfo.Top + 23);
					graphics.DrawString(strAccountNumber, _fontNormal, _brush, rectPaymentInfo.Left + 9 + strBankNameWidth + bankNameWidth + strSwiftWidth + swiftWidth, rectPaymentInfo.Top + 23);
					graphics.DrawString(_seller.BankAccountNumber, _fontBold, _brush, rectPaymentInfo.Left + 11 + strBankNameWidth + bankNameWidth + strSwiftWidth + swiftWidth + strAccountNumberWidth, rectPaymentInfo.Top + 23);
				}
				else
				{
					graphics.DrawString(strAccountNumber, _fontNormal, _brush, rectPaymentInfo.Left + 7 + strBankNameWidth + bankNameWidth, rectPaymentInfo.Top + 23);
					graphics.DrawString(_seller.BankAccountNumber, _fontBold, _brush, rectPaymentInfo.Left + 9 + strBankNameWidth + bankNameWidth + strAccountNumberWidth, rectPaymentInfo.Top + 23);
				}
			}
		}

		private double DrawProductsHeader(PdfPage page, double position, bool drawGeneralOnly)
		{
			using (XGraphics graphics = XGraphics.FromPdfPage(page))
			{
				XTextFormatter textFormatter = new XTextFormatter(graphics);
				textFormatter.Alignment = XParagraphAlignment.Center;

				if (drawGeneralOnly == false)
				{
					XRect rectNumber = new XRect(28, position, 20, 18);
					XRect rectName = new XRect(48, position, 155, 18);
					XRect rectPkwiu = new XRect(203, position, 57, 18);
					XRect rectQuantity = new XRect(260, position, 31, 18);
					XRect rectMeasureUnit = new XRect(291, position, 32, 18);
					XRect rectNetPrice = new XRect(323, position, 51, 18);

					graphics.DrawRectangle(_pen, XBrushes.LightGray, rectNumber);
					graphics.DrawRectangle(_pen, XBrushes.LightGray, rectName);
					graphics.DrawRectangle(_pen, XBrushes.LightGray, rectPkwiu);
					graphics.DrawRectangle(_pen, XBrushes.LightGray, rectQuantity);
					graphics.DrawRectangle(_pen, XBrushes.LightGray, rectMeasureUnit);
					graphics.DrawRectangle(_pen, XBrushes.LightGray, rectNetPrice);

					graphics.DrawString("Lp.", _fontProductsHeader, _brush, rectNumber, XStringFormats.Center);
					graphics.DrawString("Nazwa", _fontProductsHeader, _brush, rectName, XStringFormats.Center);
					graphics.DrawString("PKWiU", _fontProductsHeader, _brush, rectPkwiu, XStringFormats.Center);
					graphics.DrawString("Ilość", _fontProductsHeader, _brush, rectQuantity, XStringFormats.Center);
					graphics.DrawString("Jm", _fontProductsHeader, _brush, rectMeasureUnit, XStringFormats.Center);
					textFormatter.DrawString("Cena\nnetto", _fontProductsHeader, _brush, rectNetPrice);
				}

				XRect rectNetValue = new XRect(374, position, 51, 18);
				XRect rectVatRate = new XRect(425, position, 37, 18);
				XRect rectVatValue = new XRect(462, position, 51, 18);
				XRect rectGrossValue = new XRect(513, position, 51, 18);

				graphics.DrawRectangle(_pen, XBrushes.LightGray, rectNetValue);
				graphics.DrawRectangle(_pen, XBrushes.LightGray, rectVatRate);
				graphics.DrawRectangle(_pen, XBrushes.LightGray, rectVatValue);
				graphics.DrawRectangle(_pen, XBrushes.LightGray, rectGrossValue);
				
				textFormatter.DrawString("Wartość\nnetto", _fontProductsHeader, _brush, rectNetValue);
				textFormatter.DrawString("Stawka\nVAT", _fontProductsHeader, _brush, rectVatRate);
				textFormatter.DrawString("Kwota\nVAT", _fontProductsHeader, _brush, rectVatValue);
				textFormatter.DrawString("Wartość\nbrutto", _fontProductsHeader, _brush, rectGrossValue);

				return position + 18;
			}
		}

		private double DrawProduct(PdfPage page, double position, Product product, int productNumber)
		{
			using (XGraphics graphics = XGraphics.FromPdfPage(page))
			{
				XRect rectProductNumber = new XRect(28, position, 20, 12);
				XRect rectProductName = new XRect(48, position, 155, 12);
				XRect rectProductPkwiu = new XRect(203, position, 57, 12);
				XRect rectProductQuantity = new XRect(260, position, 31, 12);
				XRect rectProductMeasureUnit = new XRect(291, position, 32, 12);
				XRect rectProductNetPrice = new XRect(323, position, 51, 12);
				XRect rectProductNetValue = new XRect(374, position, 51, 12);
				XRect rectProductVatRate = new XRect(425, position, 37, 12);
				XRect rectProductVatValue = new XRect(462, position, 51, 12);
				XRect rectProductGrossValue = new XRect(513, position, 51, 12);

				graphics.DrawRectangle(_pen, rectProductNumber);
				graphics.DrawRectangle(_pen, rectProductName);
				graphics.DrawRectangle(_pen, rectProductPkwiu);
				graphics.DrawRectangle(_pen, rectProductQuantity);
				graphics.DrawRectangle(_pen, rectProductMeasureUnit);
				graphics.DrawRectangle(_pen, rectProductNetPrice);
				graphics.DrawRectangle(_pen, rectProductNetValue);
				graphics.DrawRectangle(_pen, rectProductVatRate);
				graphics.DrawRectangle(_pen, rectProductVatValue);
				graphics.DrawRectangle(_pen, rectProductGrossValue);

				XStringFormat formatNear = new XStringFormat();
				formatNear.Alignment = XStringAlignment.Near;
				formatNear.LineAlignment = XLineAlignment.Center;
				XStringFormat formatFar = new XStringFormat();
				formatFar.Alignment = XStringAlignment.Far;
				formatFar.LineAlignment = XLineAlignment.Center;

				string strProductNumber = productNumber.ToString() + ".";
				string strNetPrice = product.NetPrice.Value.ToString("N");
				string strNetValue = product.NetValue.Value.ToString("N");
				string strVatValue = product.VatValue.Value.ToString("N");
				string strGrossValue = product.GrossValue.Value.ToString("N");

				graphics.TranslateTransform(2, 0); // prevents the text from touching the left hand side borders of the cells
				graphics.DrawString(strProductNumber, GetNormalFont(strProductNumber, 16, graphics), _brush, rectProductNumber, formatNear);
				graphics.DrawString(product.Name, GetNormalFont(product.Name, 151, graphics), _brush, rectProductName, formatNear);

				graphics.TranslateTransform(-4, 0); // prevents the text from touching the right hand side borders of the cells
				graphics.DrawString(product.Pkwiu, GetNormalFont(product.Pkwiu, 53, graphics), _brush, rectProductPkwiu, formatFar);
				graphics.DrawString(product.Quantity.ToString(), GetNormalFont(product.Quantity.ToString(), 27, graphics), _brush, rectProductQuantity, formatFar);
				graphics.DrawString(product.MeasureUnit, GetNormalFont(product.MeasureUnit, 28, graphics), _brush, rectProductMeasureUnit, formatFar);
				graphics.DrawString(strNetPrice, GetNormalFont(strNetPrice, 47, graphics), _brush, rectProductNetPrice, formatFar);
				graphics.DrawString(strNetValue, GetNormalFont(strNetValue, 47, graphics), _brush, rectProductNetValue, formatFar);
				graphics.DrawString(product.VatRate, GetNormalFont(product.VatRate, 33, graphics), _brush, rectProductVatRate, formatFar);
				graphics.DrawString(strVatValue, GetNormalFont(strVatValue, 47, graphics), _brush, rectProductVatValue, formatFar);
				graphics.DrawString(strGrossValue, GetNormalFont(strGrossValue, 47, graphics), _brush, rectProductGrossValue, formatFar);

				return position + 12;
			}
		}

		private double DrawGeneralAmountsInfo(PdfPage page, double position, decimal netValue, decimal vatValue, decimal grossValue)
		{
			using (XGraphics graphics = XGraphics.FromPdfPage(page))
			{
				XRect rectHeader = new XRect(323, position, 51, 12);
				XRect rectNetValue = new XRect(374, position, 51, 12);
				XRect rectVatRate = new XRect(425, position, 37, 12);
				XRect rectVatValue = new XRect(462, position, 51, 12);
				XRect rectGrossValue = new XRect(513, position, 51, 12);

				graphics.DrawRectangle(_pen, XBrushes.LightGray, rectHeader);
				graphics.DrawRectangle(_pen, rectNetValue);
				graphics.DrawRectangle(_pen, rectVatRate);
				graphics.DrawRectangle(_pen, rectVatValue);
				graphics.DrawRectangle(_pen, rectGrossValue);

				graphics.DrawString("RAZEM", _fontProductsHeader, _brush, rectHeader, XStringFormats.Center);
				graphics.DrawString("X", _fontNormal, _brush, rectVatRate, XStringFormats.Center);

				XStringFormat formatFar = new XStringFormat();
				formatFar.Alignment = XStringAlignment.Far;
				formatFar.LineAlignment = XLineAlignment.Center;

				graphics.TranslateTransform(-2, 0); // prevents the text from touching the right hand side borders of the cells
				graphics.DrawString(netValue.ToString("N"), GetNormalFont(netValue.ToString("N"), 47, graphics), _brush, rectNetValue, formatFar);
				graphics.DrawString(vatValue.ToString("N"), GetNormalFont(vatValue.ToString("N"), 47, graphics), _brush, rectVatValue, formatFar);
				graphics.DrawString(grossValue.ToString("N"), GetNormalFont(grossValue.ToString("N"), 47, graphics), _brush, rectGrossValue, formatFar);

				return position + 12;
			}
		}

		private void DrawVatRatesInfoHeader(PdfPage page, double position, int rowsNumber)
		{
			using (XGraphics graphics = XGraphics.FromPdfPage(page))
			{
				XRect rectHeader = new XRect(323, position, 51, 12 * rowsNumber);
				graphics.DrawRectangle(_pen, XBrushes.LightGray, rectHeader);
				graphics.DrawString("W tym", _fontProductsHeader, _brush, rectHeader, XStringFormats.TopCenter);
			}
		}

		private double DrawVatRatesInfo(PdfPage page, double position, decimal netValue, string vatRate, decimal vatValue, decimal grossValue)
		{
			using (XGraphics graphics = XGraphics.FromPdfPage(page))
			{
				XRect rectNetValue = new XRect(374, position, 51, 12);
				XRect rectVatRate = new XRect(425, position, 37, 12);
				XRect rectVatValue = new XRect(462, position, 51, 12);
				XRect rectGrossValue = new XRect(513, position, 51, 12);

				graphics.DrawRectangle(_pen, rectNetValue);
				graphics.DrawRectangle(_pen, rectVatRate);
				graphics.DrawRectangle(_pen, rectVatValue);
				graphics.DrawRectangle(_pen, rectGrossValue);

				XStringFormat formatFar = new XStringFormat();
				formatFar.Alignment = XStringAlignment.Far;
				formatFar.LineAlignment = XLineAlignment.Center;

				graphics.TranslateTransform(-2, 0); // prevents the text from touching the right hand side borders of the cells
				graphics.DrawString(netValue.ToString("N"), GetNormalFont(netValue.ToString("N"), 47, graphics), _brush, rectNetValue, formatFar);
				graphics.DrawString(vatRate.ToString(), GetNormalFont(vatRate, 33, graphics), _brush, rectVatRate, formatFar);
				graphics.DrawString(vatValue.ToString("N"), GetNormalFont(vatValue.ToString("N"), 47, graphics), _brush, rectVatValue, formatFar);
				graphics.DrawString(grossValue.ToString("N"), GetNormalFont(grossValue.ToString("N"), 47, graphics), _brush, rectGrossValue, formatFar);

				return position + 12;
			}
		}

		private double DrawPaymentInfo(PdfPage page, double position, double toPayInWordsHeight)
		{
			using (XGraphics graphics = XGraphics.FromPdfPage(page))
			{
				XRect rectToPay = new XRect(28, position, 175, 28);
				XRect rectPaid = new XRect(206, position, 130, 28);
				XRect rectLeftToPay = new XRect(339, position, 175, 28);

				graphics.DrawRectangle(_pen, rectToPay);
				graphics.DrawRectangle(XPens.White, rectPaid);
				graphics.DrawRectangle(XPens.White, rectLeftToPay);

				string toPay = "Razem do zapłaty: " + _invoice.ToPay.Value.ToString("N") + " " + _invoice.PaymentCurrency;
				string paid = "Zapłacono: " + _invoice.Paid.Value.ToString("N") + " " + _invoice.PaymentCurrency;
				string leftToPay = "Pozostało do zapłaty: " + _invoice.LeftToPay.Value.ToString("N") + " " + _invoice.PaymentCurrency;

				graphics.DrawString(toPay, GetNormalFont(toPay, 166, graphics), _brush, rectToPay.Left + 5, rectToPay.Top + 17);
				graphics.DrawString(paid, GetNormalFont(paid, 121, graphics), _brush, rectPaid.Left + 5, rectPaid.Top + 17);
				graphics.DrawString(leftToPay, GetNormalFont(leftToPay, 166, graphics), _brush, rectLeftToPay.Left + 5, rectLeftToPay.Top + 17);

				position += 35;

				XRect rectBorder = new XRect(28, position, 536, toPayInWordsHeight + 20);
				XRect rectToPayInWords = new XRect(rectBorder.Left + 5, rectBorder.Top + 9, 526, toPayInWordsHeight + 1);
				graphics.DrawRectangle(_pen, rectBorder);

				XTextFormatter textFormatter = new XTextFormatter(graphics);
				textFormatter.DrawString("Słownie: " + _invoice.ToPayInWords, _fontNormal, _brush, rectToPayInWords);

				return position + toPayInWordsHeight + 20;
			}
		}

		private double DrawRemarks(PdfPage page, double position, double textHeight)
		{
			using (XGraphics graphics = XGraphics.FromPdfPage(page))
			{
				XRect rectBorder = new XRect(28, position, 536, textHeight + 20);
				XRect rectRemarks = new XRect(rectBorder.Left + 5, rectBorder.Top + 9, 526, textHeight + 1);
				graphics.DrawRectangle(_pen, rectBorder);

				XTextFormatter textFormatter = new XTextFormatter(graphics);
				textFormatter.DrawString("Uwagi: " + _invoice.Remarks, _fontNormal, _brush, rectRemarks);

				return position + textHeight + 20;
			}
		}

		private void DrawSignatures(PdfPage page, double position)
		{
			using (XGraphics graphics = XGraphics.FromPdfPage(page))
			{
				XRect rectBuyerSignature = new XRect(56, position, 214, 70);
				XRect rectSellerSignature = new XRect(322, position, 214, 70);

				graphics.DrawRectangle(_pen, rectBuyerSignature);
				graphics.DrawRectangle(_pen, rectSellerSignature);

				graphics.DrawString("imię, nazwisko i podpis osoby upoważnionej do odebrania dokumentu", _fontSubtitle, _brush, rectBuyerSignature, XStringFormats.BottomCenter);
				graphics.DrawString("imię, nazwisko i podpis osoby upoważnionej do wystawienia dokumentu", _fontSubtitle, _brush, rectSellerSignature, XStringFormats.BottomCenter);

				XFont fontCustomersSignature = new XFont("Arial", 11, XFontStyle.Regular, new XPdfFontOptions(PdfFontEncoding.Unicode));
				while (graphics.MeasureString(_customer.CustomerName, fontCustomersSignature).Width > 200)
					fontCustomersSignature = new XFont("Arial", fontCustomersSignature.Size - 0.01, XFontStyle.Regular, new XPdfFontOptions(PdfFontEncoding.Unicode));

				XFont fontSellersSignature = new XFont("Arial", 11, XFontStyle.Regular, new XPdfFontOptions(PdfFontEncoding.Unicode));
				while (graphics.MeasureString(_seller.SellerName, fontSellersSignature).Width > 200)
					fontSellersSignature = new XFont("Arial", fontSellersSignature.Size - 0.01, XFontStyle.Regular, new XPdfFontOptions(PdfFontEncoding.Unicode));

				graphics.TranslateTransform(0, -12);
				graphics.DrawString(_customer.CustomerName, fontCustomersSignature, _brush, rectBuyerSignature, XStringFormats.BottomCenter);
				graphics.DrawString(_seller.SellerName, fontSellersSignature, _brush, rectSellerSignature, XStringFormats.BottomCenter);
			}
		}

		private void DrawFooters()
		{
			for (int i = 0; i != _document.PageCount; i++)
			{
				PdfPage page = _document.Pages[i];

				using (XGraphics graphics = XGraphics.FromPdfPage(page))
				{
					graphics.DrawLine(_pen, new XPoint(28, page.Height - 26), new XPoint(page.Width - 30.75, page.Height - 26));

					XRect rectFooter = new XRect(28, page.Height - 25, page.Width - 59, 7);

					XStringFormat formatNear = new XStringFormat();
					formatNear.Alignment = XStringAlignment.Near;
					XStringFormat formatFar = new XStringFormat();
					formatFar.Alignment = XStringAlignment.Far;

					graphics.DrawString("Faktura wygenerowana przez program InvoiceInvoker", _fontSubtitle, _brush, rectFooter, formatNear);
					graphics.DrawString("Strona " + (i + 1).ToString() + " / " + _document.PageCount.ToString(), _fontSubtitle, _brush, rectFooter, formatFar);
				}
			}
		}

		private XFont GetNormalFont(string text, double width, XGraphics graphics)
		{
			XFont result = _fontNormal;
			while (graphics.MeasureString(text, result).Width > width)
				result = new XFont("Arial", result.Size - 0.01, XFontStyle.Regular, new XPdfFontOptions(PdfFontEncoding.Unicode));

			return result;
		}

		private XFont GetBoldFont(string text, double width, XGraphics graphics)
		{
			XFont result = _fontBold;
			while (graphics.MeasureString(text, result).Width > width)
				result = new XFont("Arial", result.Size - 0.01, XFontStyle.Bold, new XPdfFontOptions(PdfFontEncoding.Unicode));

			return result;
		}

		private double MeasureTextHeight(PdfPage page, string text)
		{
			using (XGraphics graphics = XGraphics.FromPdfPage(page))
			{
				string[] words = text.Split(' ');
				string line = words[0];
				int lines = 1;
				double lineHeight = graphics.MeasureString("text", _fontNormal).Height;

				for (int i = 0; i != words.Length; i++)
				{
					line += " " + words[i];

					if (graphics.MeasureString(line, _fontNormal).Width >= 526)
					{
						line = words[i];
						lines++;
					}
				}

				return lineHeight * lines;
			}
		}

		public void ShowPreview()
		{
			_document.Save("invoice_preview.pdf");
			Process.Start("invoice_preview.pdf");
		}

		public void SaveDocument(string fileName, bool show)
		{
			_document.Save(fileName);

			if (show)
				Process.Start(fileName);
		}
	}
}
